
import { ESGeoExtrudedPolygonUFMSFromLineString } from '.';
import { CzmESGeoExtrudedPolygonUFMS } from '../ESGeoExtrudedPolygonUFMS/CzmESGeoExtrudedPolygonUFMS';
import * as turf from '@turf/turf';
import { ESGeoLineString, ESJEditingMode, } from 'earthsdk3';
import { ESCesiumViewer, } from 'earthsdk3-cesium';
import { bind, createNextAnimateFrameEvent, track } from "earthsdk3";

export class CzmESGeoExtrudedPolygonUFMSFromLineString<T extends ESGeoExtrudedPolygonUFMSFromLineString = ESGeoExtrudedPolygonUFMSFromLineString> extends CzmESGeoExtrudedPolygonUFMS<T> {
    static override readonly type = this.register<ESGeoExtrudedPolygonUFMSFromLineString, ESCesiumViewer>("ESCesiumViewer", ESGeoExtrudedPolygonUFMSFromLineString.type, this);
    private _innerLineString = this.ad(new ESGeoLineString());
    get innerLineString() {
        return this._innerLineString;
    }

    constructor(sceneObject: T, czmViewer: ESCesiumViewer) {
        super(sceneObject, czmViewer);
        const viewer = czmViewer.viewer;
        if (!viewer) {
            console.warn(`viewer is undefined!`);
            return;
        }
        this._innerLineString.show = false;
        {
            czmViewer.add(this._innerLineString);
            this.ad(() => {
                czmViewer.delete(this._innerLineString);
            })
        }
        {
            this.ad(czmViewer.editingEvent.don((e) => {
                if (e.objectIDs.includes(this.innerLineString.id) && e.type === 'end' && sceneObject.positionEditing) {
                    sceneObject.positionEditing = false;
                }
            }))
            const update = () => {
                if (sceneObject.positionEditing) {
                    const mode = [
                        ESJEditingMode.LineStringAppend,
                        ESJEditingMode.LineStringInsert
                    ]
                    if (this.innerLineString.points && this.innerLineString.points.length > 2) {
                        mode.reverse();
                    }
                    this.viewer?.startEditing(this.innerLineString, mode);
                } else {
                    this.viewer?.stopEditing()
                }
            };
            update();
            this.ad(sceneObject.positionEditingChanged.don(update));
        }
    
        {
            // this.ad(bind([this._innerLineString, 'editing'], [sceneObject, 'positionEditing']));
            this.ad(bind([this._innerLineString, 'points'], [sceneObject, 'linePoints']));
        }
        {
            const debounce = (fn: Function, delay: number) => {
                var timer: NodeJS.Timeout; // 维护一个 timer
                var _this = this; // 取debounce执行作用域的this
                return function () {
                    var args = arguments;
                    if (timer) {
                        clearTimeout(timer);
                    }
                    timer = setTimeout(function () {
                        fn.apply(_this, args); // 用apply指向调用debounce的对象，相当于_this.fn(args);
                    }, delay);
                };
            };
            const update = debounce(() => {
                sceneObject.points = this._innerLineString.points && this._getBufferCoordinates(this._innerLineString.points);
                sceneObject.show = !(!sceneObject.points || sceneObject.points.length == 0);
            }, 200);
            if (this._innerLineString.points && this._innerLineString.points.length > 0) {
                update();
            }
            const event = this.ad(createNextAnimateFrameEvent(
                sceneObject.widthChanged,
                this._innerLineString.pointsChanged
            ))
            this.ad(event.don(update));
        }
        {
            const update = () => {
                sceneObject.show = !(!sceneObject.points || sceneObject.points.length == 0);
                if (!sceneObject.points || sceneObject.points.length == 0) {
                    this._innerLineString.points = undefined;
                }
            }
            update()
            this.ad(sceneObject.pointsChanged.don(update))
        }
        {
            const update = () => {
                sceneObject.show = !(!sceneObject.points || sceneObject.points.length == 0);
            }
            update()
            this.ad(sceneObject.positionEditingChanged.don(update))
        }
    }
    private _getBufferCoordinates(points: [number, number, number][]): [number, number, number][] | undefined {
        // 1.缓冲点
        // 2.找到离线段最近的点
        // 3.判断在坐标点那条线段上
        // 4.获取比值，起点加比值乘高程差
        const { sceneObject, czmViewer } = this;
        if (!points || points.length < 2 || !czmViewer.viewer) return;
        const heightDifference = points.map((item, index) => {
            if (index == 0) return 0;
            return item[2] - points[index - 1][2];
        })
        heightDifference.shift();
        const tempPos = [] as [number, number, number][];
        const lineString = turf.lineString(points);
        // 缓冲点
        let result = turf.buffer(lineString, ((sceneObject.width ?? ESGeoExtrudedPolygonUFMSFromLineString.defaults.width) / 2), {
            units: 'meters',
        });
        for (let i = 0; i < result.geometry.coordinates[0].length; i++) {
            const item = result.geometry.coordinates[0][i] as unknown as [number, number];
            // 找最近点
            const nearest = turf.nearestPointOnLine(lineString, turf.point(item)).geometry.coordinates as unknown as [number, number];
            // 判定在那条线段
            for (let j = 0; j < points.length - 1; j++) {
                const isPointOnLine = turf.booleanPointOnLine(nearest, turf.lineString([[points[j][0], points[j][1]], [points[j + 1][0], points[j + 1][1]]]), {
                    ignoreEndVertices: false,
                });
                if (isPointOnLine) {
                    const distance1 = turf.distance(turf.point(points[j]), nearest, { units: 'meters' });
                    const distance2 = turf.distance(turf.point(points[j]), turf.point(points[j + 1]), { units: 'meters' });
                    tempPos.push([...item, points[j][2] + heightDifference[j] * (distance1 / distance2)]);
                    break;
                }
                // 判断有错误，找最近点代替
                if (j == points.length - 2) {
                    let height = turf.nearestPoint(nearest, turf.featureCollection(points.map(item => turf.point(item))))
                    tempPos.push([...item, height.geometry.coordinates[2]]);
                }
                // const carto = Cesium.Cartographic.fromDegrees(...nearest, undefined, CzmViewer.getHeightsScartchCarto);
                // const height = czmViewer.viewer.scene.sampleHeight(carto);
            }
        }
        // console.log(tempPos);

        return tempPos;
    }
}